package com.tangji.signaltower.websocket.netty;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.api.Response;
import com.fqgj.common.utils.StringUtils;
import com.kly.user.enums.OnlineStatus;
import com.kly.user.service.UserService;
import com.tangji.signaltower.bean.TransBean;
import com.tangji.signaltower.constant.AttributeConstant;
import com.tangji.signaltower.constant.RedisKey;
import com.tangji.signaltower.enums.BizCodeEnum;
import com.tangji.signaltower.websocket.model.WebUser;
import com.tangji.signaltower.websocket.session.ServerSession;
import com.tangji.signaltower.websocket.session.SessionFactory;
import io.netty.channel.*;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;

public class MebooProcessorHandler extends SimpleChannelInboundHandler<Object> {

    private UserService userService;

    private RedisClient redisClient;

    private Boolean enableOnlineSwitch;

    private static final Logger logger = LoggerFactory.getLogger(MebooProcessorHandler.class);


    public MebooProcessorHandler(UserService userService, RedisClient redisClient, Boolean enableOnlineSwitch) {
        this.userService = userService;
        this.redisClient = redisClient;
        this.enableOnlineSwitch = enableOnlineSwitch;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("与客户端建立连接,通道开启,{}", this.getClientAddress(ctx));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.info("netty 异常，ip:{}，exception:{}", this.getClientAddress(ctx), cause);
//        ctx.pipeline().fireExceptionCaught(cause);
    }

    /**
     * 客户端与服务器关闭连接的时候触发
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info("与客户端断开连接,通道关闭,{}", this.getClientAddress(ctx));
        ServerSession serverSession = ServerSession.getSession(ctx);
        if (serverSession != null) {
            logger.info("==Session== channelInactive 与客户端断开连接, sessionId,{}", serverSession.getSessionId());
            SessionFactory.removeFromAllMap(serverSession);
            // 更新用户在线状态
            userService.updateOffline(serverSession.getSessionId());
            redisClient.del(RedisKey.user_online_status_key, serverSession.getSessionId());

        }

    }

    /**
     * 服务器接受客户端的数据信息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (Objects.isNull(msg)) {
            logger.info("收到消息内容为空,来源ip:{}", this.getClientAddress(ctx));
            return;
        }
        if (msg instanceof TextWebSocketFrame) {
            String message = ((TextWebSocketFrame) msg).text();
            logger.info("收到文本消息,来源ip:{},内容:{}", this.getClientAddress(ctx), message);

            processUserOnlineStatus(ctx, message);

            ctx.channel().writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(Response.ok())));
        } else if (msg instanceof BinaryWebSocketFrame) {
            logger.info("收到二进制消息,来源ip:{},内容:{}", this.getClientAddress(ctx), ((BinaryWebSocketFrame) msg).content().readableBytes());
        }
    }

    /**
     * 处理用户在线状态
     */
    private Response processUserOnlineStatus(ChannelHandlerContext ctx, String message) {

        if (!this.enableOnlineSwitch) {
            logger.info("processUserOnlineStatusSwitch: {}", enableOnlineSwitch);
            return Response.ok();
        }
        final TransBean transBean = JSONObject.parseObject(message, TransBean.class);
        if (BizCodeEnum.C10000.getBizCode().equals(transBean.getBizCode())) {

            final String token = getToken(ctx, transBean);
            final String userCode = getUserCode(ctx, transBean);

            // 添加会话
            final ServerSession serverSession = addNewSession(ctx, token, userCode);
            if (Objects.isNull(serverSession)) {
                logger.error("添加session失败,来源ip:{}", this.getClientAddress(ctx));
                return Response.ok();
            }

            Object onlineStatus = redisClient.get(RedisKey.user_online_status_key, userCode);
            if (Objects.isNull(onlineStatus)) {
                logger.info("== 更新用户状态 == userCode:{}", userCode);
                userService.updateOnline(userCode);
                // 缓存5分钟
                redisClient.set(RedisKey.user_online_status_key, OnlineStatus.ONLINE_STATUS.getValue(), 300, userCode);
            }
            return Response.ok();
        }
        return Response.ok();
    }

    private String getUserCode(ChannelHandlerContext ctx, TransBean transBean) {
        final String body = transBean.getBody();
        if (StringUtils.isEmpty(body)) {
            logger.info("接收到文本消息为空，本次不作处理,来源ip:{}", this.getClientAddress(ctx));
            return null;
        }
        final JSONObject bodyJson = JSONObject.parseObject(body);
        final String userCode = bodyJson.getString("userCode");
        if (StringUtils.isEmpty(userCode)) {
            logger.info("接收到参数：userCode 为空，本次不作处理,来源ip:{}", this.getClientAddress(ctx));
            return null;
        }
        return userCode;
    }

    private ServerSession addNewSession(ChannelHandlerContext ctx, String sessionId, String userCode) {
        if (StringUtils.isEmpty(sessionId) || StringUtils.isEmpty(userCode)) {
            logger.error("接收到参数：token 或 userCode 为空，添加session失败,来源ip:{}", this.getClientAddress(ctx));
            return null;
        }
//        ServerSession serverSession = ServerSession.getSession(ctx);
        ServerSession serverSession = SessionFactory.getSession(sessionId);

        if (serverSession == null) {
            ServerSession oldSession = ServerSession.getSession(ctx.channel());
            ServerSession newServerSession = new ServerSession(ctx.channel(), sessionId).setWebUser(new WebUser(sessionId, userCode));
            SessionFactory.updateSession(oldSession, newServerSession);
            logger.info("==Session== addNewSession, 绑定成功,channelId:{},token:{},sessionId:{}", newServerSession.getChannel().id().asLongText(), sessionId, sessionId);
            return newServerSession;
        }

        return serverSession;
    }

    private String getToken(ChannelHandlerContext ctx, TransBean transBean) {
        final String head = transBean.getHead();
        if (StringUtils.isEmpty(head)) {
            logger.error("接收到文本请求头[head]为空，本次不作处理,来源ip:{}", this.getClientAddress(ctx));
            return null;
        }
        final JSONObject headJson = JSONObject.parseObject(head);
        final String token = headJson.getString("token");
        if (StringUtils.isEmpty(token)) {
            logger.error("接收到文本请求头[token]为空，本次不作处理,来源ip:{}", this.getClientAddress(ctx));
            return null;
        }
        return token;
    }


    /**
     * 根据token发消息
     */
    public String sendMessage(String sessionId, String message) {
        ServerSession serverSession = SessionFactory.getSession(sessionId);

        if (serverSession != null) {
            Channel channel = serverSession.getChannel();
            if (serverSession.getChannel().isActive()) {
                final ChannelFuture channelFuture = channel.writeAndFlush(new TextWebSocketFrame(message));
                if (channelFuture != null) {
                    channelFuture.addListener((ChannelFutureListener) future
                            -> logger.info("===推送消息成功1，sessionId:{}, message{}", sessionId, message));
                }
                logger.info("===推送消息成功2，sessionId:{}, message{}", sessionId, message);
                return "success";
            } else {
                logger.error("===推送消息失败，当前channel状态未处理active状态，sessionId:{}, message{}", sessionId, message);
            }
        } else {
            logger.error("===推送消息失败，无法获取session，sessionId:{}, message{}", sessionId, message);
        }
        return "failure";
    }


    private String getClientAddress(ChannelHandlerContext ctx) {
        return getClientIp(ctx);
    }

    private String getClientIp(ChannelHandlerContext ctx) {
        try {
            return ctx.channel().attr(AttributeConstant.CLIENT_IP).get();
        } catch (Exception e) {
            logger.warn("获取ip失败,{}", ctx.channel().remoteAddress());
            return ctx.channel().remoteAddress().toString();
        }
    }


}
